package com.niiwoo.civet.trade.service.local.creditor;

import java.util.List;
import java.util.concurrent.CompletableFuture;

import org.joda.time.DateTime;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import com.niiwoo.civet.trade.dao.entity.CreditorSaleDetail;
import com.niiwoo.civet.trade.dao.entity.InvestorCreditor;
import com.niiwoo.civet.trade.dao.entity.ProjectInvestOrder;
import com.niiwoo.civet.trade.dao.mapper.CreditorSaleDetailMapperExt;
import com.niiwoo.civet.trade.dao.mapper.CreditorSellingMapperExt;
import com.niiwoo.civet.trade.dao.mapper.InvestorCreditorMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectInvestOrderMapperExt;
import com.niiwoo.civet.trade.enums.InvestorCreditorStatusEnum;
import com.niiwoo.civet.trade.enums.ProjectInvestOrderEvolutionStatusEnum;
import com.niiwoo.civet.trade.enums.creditorSale.SaleDetailEndTypeEnum;
import com.niiwoo.civet.trade.enums.creditorSale.SaleDetailStatusEnum;
import com.niiwoo.tripod.provider.exception.BizException;

import lombok.extern.slf4j.Slf4j;

/**
 * 债权出让撤销相关 
 *
 * @author kevin.jia
 * @create 2018年7月4日 上午11:11:28
 */
@Slf4j
@Service
public class CreditorSaleOrderCancelService {
	
	@Autowired
	private CreditorSaleDetailMapperExt creditorSaleDetailMapperExt;
	
	@Autowired
	private ProjectInvestOrderMapperExt projectInvestOrderMapperExt;
	
	@Autowired
	private InvestorCreditorMapperExt investorCreditorMapperExt;
	
	@Autowired
	private CreditorSellingMapperExt creditorSellingMapperExt;

	@Autowired
	private CreditorSaleOrderToLanMaoService creditorSaleOrderToLanMaoService;
	
	@Autowired
	private TradableInvestOrderService tradableInvestOrderService;
	
	/**
	 * 撤销债权出让总入口
	 * 
	 * @param requestDTO
	 * @param
	 */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
	public void cancelCreditorSaleOrder(Long creditorSaleDetailId, String userId, SaleDetailEndTypeEnum endTypeEnum) {
    	
		log.info("###取消债权出让###,请求开始, UserId:{}, 债权出让详情ID:{}", userId, creditorSaleDetailId);
		
		final CreditorSaleDetail creditorSaleDetail = creditorSaleDetailMapperExt.selectByPrimaryKeyAndUserIdForUpdate(creditorSaleDetailId, userId);
		
		Assert.notNull(creditorSaleDetail, "###取消债权出让###债权出让记录不存在");
		
		if (SaleDetailStatusEnum.SALE_END.getValue().equals(creditorSaleDetail.getStatus())) {
			log.error("###取消债权出让###债权出让订单已取消无需处理, 债权出让详情ID:{}", creditorSaleDetailId);
			return;
		}
		if (SaleDetailStatusEnum.SALE_APPLYING.getValue().equals(creditorSaleDetail.getStatus())){
			log.error("###取消债权出让###债权转让申请正在处理中，无法撤销， ID:{}", creditorSaleDetail.getId());
			throw new BizException("TRD2000004");
		}
		if (SaleDetailStatusEnum.PRE_CREDIT_ASSIGNMENT_HANDING.getValue().equals(creditorSaleDetail.getStatus())
				|| SaleDetailStatusEnum.CONFIRM_CREDIT_ASSIGNMENT_HANDING.getValue().equals(creditorSaleDetail.getStatus())) {
			log.error("###取消债权出让###投资锁定中，无法撤销， ID:{}", creditorSaleDetail.getId());
			throw new BizException("TRD2000003");
		}
		if(SaleDetailStatusEnum.SALE_SUCCESS.getValue().equals(creditorSaleDetail.getStatus())){
			log.error("###取消债权出让###转让成功，无法撤销， ID:{}", creditorSaleDetail.getId());
			throw new BizException("TRD2000001");
		}
		
		// 取消本地债权出让订单
		cancelCreditorSaleOrderLocalHandle(creditorSaleDetail, endTypeEnum.getValue());

		// 同步到银行存管
		TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
			@Override
			public void afterCommit() {
				CompletableFuture.runAsync(() -> {
					creditorSaleOrderToLanMaoService.invokeLanMaoCancelDebentureSale(creditorSaleDetail);
					
					//重新构建对象
					CreditorSaleDetail tmp = new CreditorSaleDetail();
					tmp.setId(creditorSaleDetail.getId());
					tmp.setUserId(creditorSaleDetail.getUserId());
					tmp.setStatus(SaleDetailStatusEnum.SALE_END.getValue());
					tmp.setEndType(endTypeEnum.getValue());
					tmp.setSaleAmount(creditorSaleDetail.getSaleAmount());
					tradableInvestOrderService.sendDebtTransferFailureMessage(tmp);
				});
			}
		});
		log.info("###取消债权出让###,请求结束, 债权出让详情ID:{}", creditorSaleDetailId);
	}
    
	/**
	 * 自动取消超时未出让成功债权出让订单
	 */
	public void autoCancelCreditorSale() {
		Long beginTime = System.currentTimeMillis();
		log.info("###取消债权出让###自动取消超时未出让成功的债权出让订单，任务开始");
		List<CreditorSaleDetail> creditorSaleDetailList = creditorSaleDetailMapperExt.selectTimeoutToCancelled();
		if (CollectionUtils.isEmpty(creditorSaleDetailList)) {
			log.info("###取消债权出让###自动取消超时未出让成功的债权出让订单，本次任务待处理记录为空，任务结束");
			return;
		}
		log.info("###取消债权出让###自动取消超时未出让成功的债权出让订单，本次任务待处理记录数：{}", creditorSaleDetailList.size());
		// 循环调用取消接口
		for (CreditorSaleDetail creditorSaleDetail : creditorSaleDetailList) {
			try {
				getProxyObject().cancelCreditorSaleOrder(creditorSaleDetail.getId(), creditorSaleDetail.getUserId(), SaleDetailEndTypeEnum.EXPIRE);
			} catch (Exception e) {
				log.error("###取消债权出让###自动取消超时未出让成功的债权出让订单异常，CreditorSaleDetailId:{}, UserId:{}", creditorSaleDetail.getId(), creditorSaleDetail.getUserId(), e);	
			}
		}
		log.info("###取消债权出让###自动取消超时未出让成功的债权出让订单，任务结束，总耗时：{}ms", System.currentTimeMillis() - beginTime);
	}
	
	/**
	 * 调用银行存管取消债权出让补偿任务
	 */
	public void cancelDebentureSaleCompensation() {
		Long beginTime = System.currentTimeMillis();
		log.info("###取消债权出让补偿###调用银行存管取消债权出让补偿任务，任务开始");
		List<CreditorSaleDetail> creditorSaleDetailList = creditorSaleDetailMapperExt.selectNeedCompensationForCancelDebentureSale();
		if (CollectionUtils.isEmpty(creditorSaleDetailList)) {
			log.info("###取消债权出让补偿###调用银行存管取消债权出让补偿任务，本次任务待处理记录为空，任务结束");
			return;
		}
		log.info("###取消债权出让补偿###调用银行存管取消债权出让补偿任务，本次任务待处理记录数：{}", creditorSaleDetailList.size());
		for (CreditorSaleDetail creditorSaleDetail : creditorSaleDetailList) {
			creditorSaleOrderToLanMaoService.invokeLanMaoCancelDebentureSaleCompensation(creditorSaleDetail);
		}
		log.info("###取消债权出让补偿###调用银行存管取消债权出让补偿任务，任务结束，总耗时：{}ms", System.currentTimeMillis() - beginTime);
	}

	/**
	 * 调用银行存管成功后置处理
	 * 
	 * @param creditorSaleDetail
	 */
	private void cancelCreditorSaleOrderLocalHandle(CreditorSaleDetail creditorSaleDetail, Byte endType) {
		DateTime currentDate = new DateTime();
		
		// 更新债权出让订单
		CreditorSaleDetail updateCreditorSaleDetail = new CreditorSaleDetail();
		updateCreditorSaleDetail.setId(creditorSaleDetail.getId());
		updateCreditorSaleDetail.setUserId(creditorSaleDetail.getUserId());
		updateCreditorSaleDetail.setStatus(SaleDetailStatusEnum.SALE_END.getValue());
		updateCreditorSaleDetail.setBankSyncStatus((byte) 1);
		updateCreditorSaleDetail.setEndType(endType);
		updateCreditorSaleDetail.setEndTime(currentDate.toDate());
		updateCreditorSaleDetail.setUpdateTime(currentDate.toDate());
		creditorSaleDetailMapperExt.updateByPrimaryKeyAndUserIdSelective(updateCreditorSaleDetail);
		
		// 更新投资债权回款中
		InvestorCreditor investorCreditor = investorCreditorMapperExt.selectByInvestOrderId(creditorSaleDetail.getInvestOrderId());
		InvestorCreditor updateInvestorCreditor = new InvestorCreditor();
		updateInvestorCreditor.setId(investorCreditor.getId());
		updateInvestorCreditor.setStatus(InvestorCreditorStatusEnum.NORMAL_RECEIVING.getStatus());
		updateInvestorCreditor.setUpdateTime(currentDate.toDate());
		investorCreditorMapperExt.updateByPrimaryKeySelective(updateInvestorCreditor);
		
		// 更新投资订单
		ProjectInvestOrder updateProjectInvestOrder = new ProjectInvestOrder();
		updateProjectInvestOrder.setId(creditorSaleDetail.getInvestOrderId());
		updateProjectInvestOrder.setEvolutionStatus(ProjectInvestOrderEvolutionStatusEnum.BACKING.getValue());
		updateProjectInvestOrder.setUpdateTime(currentDate.toDate());
		projectInvestOrderMapperExt.updateByPrimaryKeySelective(updateProjectInvestOrder);
		
		// 删除可投资债权订单记录
		creditorSellingMapperExt.deleteByPrimaryKey(creditorSaleDetail.getId());
	}

	private CreditorSaleOrderCancelService getProxyObject() {
		return (CreditorSaleOrderCancelService) AopContext.currentProxy();
	}
}